#include "nand_gd5.h"


#define gd5_spi_cs             nand_spi_cs
#define gd5_spi_mode           nand_spi_mode
#define gd5_spi_wb             nand_spi_wb
#define gd5_spi_rb             nand_spi_rb
#define gd5_spi_cmd_send       nand_spi_cmd_send
#define gd5_spi_read           nand_spi_read
#define gd5_spi_write          nand_spi_write
#define gd5_qpi_read           nand_qpi_read
#define gd5_qpi_write          nand_qpi_write

extern volatile uint16_t nand_u_tick;

uint8_t gd5_wait_ready(void);

/*-------------------------------------------------------------*
* write enable
*--------------------------------------------------------------*/
void gd5_reset(void)
{
	/*slect chip*/
	gd5_spi_cs(NAND_CS_LOW);

	/*switch mode*/
	gd5_spi_mode(NAND_SPI_MODE);

	/*send cmd*/
	gd5_spi_wb(GD5_CMD_RESET);

	/*release chip*/
	gd5_spi_cs(NAND_CS_HIGH);

	gd5_wait_ready();
}

/*-------------------------------------------------------------*
*  featute get
*  read twice to confirm
*--------------------------------------------------------------*/
uint8_t gd5_featute_get(uint8_t addr)
{
	uint8_t st1, st2;

	for (;;)
	{
		/*slect chip*/
		gd5_spi_cs(NAND_CS_LOW);

		/*send cmd*/
		gd5_spi_wb(GD5_CMD_FEATURE_GET);
		gd5_spi_wb(addr);
		st1 = gd5_spi_rb();

		/*release chip*/
		gd5_spi_cs(NAND_CS_HIGH);

		/*slect chip*/
		gd5_spi_cs(NAND_CS_LOW);

		/*send cmd*/
		gd5_spi_wb(GD5_CMD_FEATURE_GET);
		gd5_spi_wb(addr);
		st2 = gd5_spi_rb();

		/*release chip*/
		gd5_spi_cs(NAND_CS_HIGH);

		/*confirm*/
		if (st2 == st1)
			break;
	}

	return st1;
}

/*-------------------------------------------------------------*
*  featute set
*--------------------------------------------------------------*/
void gd5_featute_set(uint8_t addr, uint8_t dat)
{
	/*slect chip*/
	gd5_spi_cs(NAND_CS_LOW);

	/*send cmd*/
	gd5_spi_wb(GD5_CMD_FEATURE_SET);
	gd5_spi_wb(addr);
	gd5_spi_wb(dat);

	/*release chip*/
	gd5_spi_cs(NAND_CS_HIGH);
}

/*-------------------------------------------------------------*
*  wait for chip ready
*--------------------------------------------------------------*/
uint8_t gd5_wait_ready(void)
{
	uint16_t tick_start;

	tick_start = nand_u_tick;

	while (1)
	{
		uint8_t st;
		st = gd5_featute_get(GD5_FEATURE_3_STATUS);
		if (0 == (st & 01))
		{
			break;
		}

		//对于GD5，最大实际忙时为5ms
		if (nand_u_tick - tick_start > 10)
			return 1;
	}

	return 0;
}

/*-------------------------------------------------------------*
*  wait for chip ready
*--------------------------------------------------------------*/
uint8_t gd5_ecc_error_get(void)
{
	uint8_t st;

	st = gd5_featute_get(GD5_FEATURE_3_STATUS);

	//[5:4]=10表示有错误且无法修复
	if ((st == 0x20) || (st == 0x30))
	{
		return 1;
	}

	return 0;
}

/*-------------------------------------------------------------*
* write enable
*--------------------------------------------------------------*/
uint8_t gd5_write_enable(void)
{
	uint8_t set_cnt = 0;
	uint8_t st;

	for (;;)
	{
		/*slect chip*/
		gd5_spi_cs(NAND_CS_LOW);

		/*send cmd*/
		gd5_spi_wb(GD5_CMD_WRITE_ENABLE);

		/*release chip*/
		gd5_spi_cs(NAND_CS_HIGH);

		/*get status*/
		st = gd5_featute_get(GD5_FEATURE_3_STATUS);

		/*write enable latch judge*/
		if (st & 0x02)
		{
			break;
		}

		set_cnt++;
		if (set_cnt > 3)
		{
			return 1;
		}
	}

	return 0;
}

/*-------------------------------------------------------------*
* write enable
*--------------------------------------------------------------*/
uint8_t gd5_write_disable(void)
{
	uint8_t set_cnt = 0;
	uint8_t st;

	for (;;)
	{
		/*slect chip*/
		gd5_spi_cs(NAND_CS_LOW);

		/*send cmd*/
		gd5_spi_wb(GD5_CMD_WRITE_DISABLE);

		/*release chip*/
		gd5_spi_cs(NAND_CS_HIGH);

		/*get status*/
		st = gd5_featute_get(GD5_FEATURE_3_STATUS);

		/*write enable latch judge*/
		if (0 == (st & 0x02))
		{
			break;
		}

		set_cnt++;
		if (set_cnt > 3)
		{
			return 1;
		}
	}

	return 0;
}

/*-------------------------------------------------------------*
*  read id
*--------------------------------------------------------------*/
void gd5_read_id(uint8_t *dat)
{
	/*slect chip*/
	gd5_spi_cs(NAND_CS_LOW);

	/*send cmd*/
	gd5_spi_wb(GD5_CMD_READ_ID);
	gd5_spi_wb(0x00);          //发送01H，读取顺序为 Dev-ID, MfrID

	*dat++ = gd5_spi_rb();
	*dat++ = gd5_spi_rb();

	/*release chip*/
	gd5_spi_cs(NAND_CS_HIGH);
}


/*-------------------------------------------------------------*
*  read page to cache
*--------------------------------------------------------------*/
uint8_t gd5_read_page(uint32_t addr)
{
	uint8_t buff[8];

	/*slect chip*/
	gd5_spi_cs(NAND_CS_LOW);

	/*send cmd*/
	buff[0] = GD5_CMD_PAGE_READ;
	buff[1] = (addr >> 16) & 0xFF;
	buff[2] = (addr >> 8) & 0xFF;
	buff[3] = (addr >> 0) & 0xFF;
	gd5_spi_cmd_send(buff, 4);

	/*release chip*/
	gd5_spi_cs(NAND_CS_HIGH);

	gd5_wait_ready();

	return 0;
}

/*-------------------------------------------------------------*
*  read page from cache
*--------------------------------------------------------------*/
uint8_t gd5_read_cache(uint16_t addr, uint8_t * dat, uint16_t Len)
{
	uint8_t buff[8];

	/*slect chip*/
	gd5_spi_cs(NAND_CS_LOW);

	/*send cmd*/
	buff[0] = GD5_CMD_CACHE_READ;
	buff[1] = (addr >> 8) & 0xFF;     //addr[15:8]
	buff[2] = (addr >> 0) & 0xFF;     //addr[7:0]
	buff[3] = 0x55;                   //Dummy
	gd5_spi_cmd_send(buff, 4);

	gd5_spi_read(dat, Len);

	/*release chip*/
	gd5_spi_cs(NAND_CS_HIGH);

	return 0;
}

/*-------------------------------------------------------------*
*  read page from cache
*--------------------------------------------------------------*/
uint8_t gd5_q_read_cache(uint16_t addr, uint8_t * dat, uint16_t Len)
{
	uint8_t buff[8];

	/*slect chip*/
	gd5_spi_cs(NAND_CS_LOW);

	/*send cmd*/
	buff[0] = GD5_CMD_CACHE_READ_X4;
	buff[1] = (addr >> 8) & 0xFF;     //addr[15:8]
	buff[2] = (addr >> 0) & 0xFF;     //addr[7:0]
	buff[3] = 0x55;                   //Dummy
	gd5_spi_cmd_send(buff, 4);

	gd5_spi_mode(NAND_QPI_READ_MODE);
	gd5_qpi_read(dat, Len);

	/*release chip*/
	gd5_spi_cs(NAND_CS_HIGH);
    
	/*switch mode*/
	gd5_spi_mode(NAND_SPI_MODE);
    
	return 0;
}

/*-------------------------------------------------------------*
*  write page to cache
*--------------------------------------------------------------*/
uint8_t gd5_write_cache(uint16_t addr, uint8_t * dat, uint16_t Len)
{
	uint8_t buff[8];

	gd5_write_enable();

	/*slect chip*/
	gd5_spi_cs(NAND_CS_LOW);

	/*send cmd*/
	buff[0] = GD5_CMD_PROGRAM_LOAD;
	buff[1] = (addr >> 8) & 0xFF;     //addr[15:8]
	buff[2] = (addr >> 0) & 0xFF;     //addr[7:0]
	gd5_spi_cmd_send(buff, 3);

	gd5_spi_write(dat, Len);

	/*release chip*/
	gd5_spi_cs(NAND_CS_HIGH);

	return 0;
}

/*-------------------------------------------------------------*
*  write page to cache
*--------------------------------------------------------------*/
uint8_t gd5_q_write_cache(uint16_t addr, uint8_t * dat, uint16_t Len)
{
	uint8_t buff[8];

	gd5_write_enable();

	/*slect chip*/
	gd5_spi_cs(NAND_CS_LOW);

	/*send cmd*/
	buff[0] = GD5_CMD_PROGRAM_LOAD_X4;
	buff[1] = (addr >> 8) & 0xFF;     //addr[15:8]
	buff[2] = (addr >> 0) & 0xFF;     //addr[7:0]
	gd5_spi_cmd_send(buff, 3);

	gd5_spi_mode(NAND_QPI_WRITE_MODE);
	gd5_qpi_write(dat, Len);

	/*release chip*/
	gd5_spi_cs(NAND_CS_HIGH);

	/*switch mode*/
	gd5_spi_mode(NAND_SPI_MODE);
    
	return 0;
}

/*-------------------------------------------------------------*
*  program exe
*--------------------------------------------------------------*/
uint8_t gd5_program_exe(uint32_t addr)
{
	uint8_t buff[8];

	gd5_write_enable();

	/*slect chip*/
	gd5_spi_cs(NAND_CS_LOW);

	/*send cmd*/
	buff[0] = GD5_CMD_PROGRAM_EXE;
	buff[1] = (addr >> 16) & 0xFF;
	buff[2] = (addr >> 8) & 0xFF;
	buff[3] = (addr >> 0) & 0xFF;
	gd5_spi_cmd_send(buff, 4);

	/*release chip*/
	gd5_spi_cs(NAND_CS_HIGH);

	gd5_wait_ready();

	return 0;
}

/*-------------------------------------------------------------*
*  program exe
*--------------------------------------------------------------*/
uint8_t gd5_block_erase(uint32_t block_addr)
{
	uint8_t buff[8];

	uint32_t addr;

	addr = block_addr * GD5_PAGE_PER_BLOCK; //指令需提供页地址

	gd5_write_enable();

	/*slect chip*/
	gd5_spi_cs(NAND_CS_LOW);

	/*send cmd*/
	buff[0] = GD5_CMD_BLOCK_ERASE;
	buff[1] = (addr >> 16) & 0xFF;
	buff[2] = (addr >> 8) & 0xFF;
	buff[3] = (addr >> 0) & 0xFF;
	gd5_spi_cmd_send(buff, 4);

	/*release chip*/
	gd5_spi_cs(NAND_CS_HIGH);

	gd5_wait_ready();

	return 0;
}

/*-------------------------------------------------------------*
*  program exe
*--------------------------------------------------------------*/
uint8_t gd5_chip_erase(void)
{
	uint16_t index;
	for (index = 0; index < GD5_BLOCK_TOTAL; index++)
	{
		gd5_block_erase(index);
	}

	return 0;
}

/*-------------------------------------------------------------*
*  program exe
*--------------------------------------------------------------*/
uint8_t gd5_block_empty_check(uint32_t block_addr)
{
	return 0;
}

/*-------------------------------------------------------------*
*  opt memory select
*--------------------------------------------------------------*/
uint8_t gd5_opt_mem_select(void)
{
	uint8_t st;

	while (1)
	{
		gd5_reset();

		//开启 ECC 和 QPI
		gd5_featute_set(GD5_FEATURE_2_CFG, 0x51);

		st = gd5_featute_get(GD5_FEATURE_2_CFG);

		//最高位 OTP_PRT 不可设置，无需验证
		if ((st & 0x51) == 0x51)
			break;
	}

	return 0;
}

/*-------------------------------------------------------------*
*  main memory select
*--------------------------------------------------------------*/
uint8_t gd5_main_mem_select(void)
{
	uint8_t st;

	while (1)
	{
		gd5_reset();

		//开启 ECC 和 QPI
		gd5_featute_set(GD5_FEATURE_2_CFG, 0x11);

		st = gd5_featute_get(GD5_FEATURE_2_CFG);

		//最高位 OTP_PRT 不可设置，无需验证
		if ((st & 0x51) == 0x11)
			break;
	}

	return 0;
}

/*-------------------------------------------------------------*
*  init
*--------------------------------------------------------------*/
uint8_t gd5_init(void)
{
	uint8_t st;

	while (1)
	{
		gd5_reset();

		gd5_wait_ready();

		st = gd5_featute_get(GD5_FEATURE_2_CFG);

		//最高位 OTP_PRT 不可设置，无需验证
		if ((st & 0x51) == 0x11)
			break;

		//开启 ECC 和 QPI
		gd5_featute_set(GD5_FEATURE_2_CFG, 0x11);
	}

	return 0;
}
